qCInfo(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Deleting nested encrypted item" << filename;
const auto deleteJob = new DeleteJob(_propagator->account(), _propagator->fullRemotePath(filename), {}, this);
+ deleteJob->setSkipTrashbin(true);
if (_encryptedFolderMetadataHandler && _encryptedFolderMetadataHandler->folderMetadata()
&& _encryptedFolderMetadataHandler->folderMetadata()->isValid()) {
deleteJob->setFolderToken(_encryptedFolderMetadataHandler->folderToken());
req.setRawHeader(oneHeaderIt.key(), oneHeaderIt.value());
}
+ if (_skipTrashbin) {
+ req.setRawHeader("X-NC-Skip-Trashbin", "true");
+ }
+
if (_url.isValid()) {
startRequest("DELETE", _url, req);
} else {
_folderToken = folderToken;
}
+bool DeleteJob::skipTrashbin() const
+{
+ return _skipTrashbin;
+}
+
+void DeleteJob::setSkipTrashbin(bool skipTrashbin)
+{
+ _skipTrashbin = skipTrashbin;
+}
+
} // namespace OCC
[[nodiscard]] QByteArray folderToken() const;
void setFolderToken(const QByteArray &folderToken);
+ [[nodiscard]] bool skipTrashbin() const;
+ void setSkipTrashbin(bool skipTrashbin);
+
private:
QMap<QByteArray, QByteArray> _headers = {};
QUrl _url; // Only used if the constructor taking a url is taken.
QByteArray _folderToken;
+ bool _skipTrashbin = false;
};
}
const bool isOnlineOnlyItem = isCfApiVfsMode && (localEntry.isDirectory || _discoveryData->_syncOptions._vfs->isDehydratedPlaceholder(_discoveryData->_localDir + path._local));
const auto isE2eeMoveOnlineOnlyItemWithCfApi = isE2eeMove && isOnlineOnlyItem;
+ if (isE2eeMove) {
+ qCInfo(lcDisco) << "requesting permanent deletion for" << originalPath;
+ _discoveryData->_permanentDeletionRequests.insert(originalPath);
+ }
+
if (isE2eeMoveOnlineOnlyItemWithCfApi) {
item->_instruction = CSYNC_INSTRUCTION_NEW;
item->_direction = SyncFileItem::Down;
job->setInsideEncryptedTree(isInsideEncryptedTree() || item->isEncrypted());
if (removed) {
job->setParent(_discoveryData);
+ _discoveryData->_deletedItem[path._original] = item;
_discoveryData->enqueueDirectoryToDelete(path._original, job);
} else {
connect(job, &ProcessDirectoryJob::finished, this, &ProcessDirectoryJob::subJobFinished);
}
}
+void DiscoveryPhase::markPermanentDeletionRequests()
+{
+ // since we don't know in advance which files/directories need to be permanently deleted,
+ // we have to look through all of them at the end of the run
+ for (const auto &originalPath : _permanentDeletionRequests) {
+ const auto it = _deletedItem.find(originalPath);
+ if (it == _deletedItem.end()) {
+ qCWarning(lcDiscovery) << "didn't find an item for" << originalPath << "(yet)";
+ continue;
+ }
+
+ auto item = *it;
+ if (!(item->_instruction == CSYNC_INSTRUCTION_REMOVE || item->_direction == SyncFileItem::Up)) {
+ qCWarning(lcDiscovery) << "will not request permanent deletion for" << originalPath << "as the instruction is not CSYNC_INSTRUCTION_REMOVE, or the direction is not Up";
+ continue;
+ }
+
+ qCInfo(lcDiscovery) << "requested permanent server-side deletion for" << originalPath;
+ item->_wantsPermanentDeletion = true;
+ }
+}
+
void DiscoveryPhase::startJob(ProcessDirectoryJob *job)
{
ENFORCE(!_currentRootJob);
auto nextJob = _queuedDeletedDirectories.take(_queuedDeletedDirectories.firstKey());
startJob(nextJob);
} else {
+ markPermanentDeletionRequests();
emit finished();
}
});
void enqueueDirectoryToDelete(const QString &path, ProcessDirectoryJob* const directoryJob);
+ /// contains files/folder names that are requested to be deleted permanently
+ QSet<QString> _permanentDeletionRequests;
+
+ void markPermanentDeletionRequests();
+
public:
// input
QString _localDir; // absolute path to the local directory. ends with '/'
}
}
- qCInfo(lcPropagateRemoteDelete) << "Deleting file, local" << _item->_file << "remote" << remoteFilename;
+ qCInfo(lcPropagateRemoteDelete) << "Deleting file, local" << _item->_file << "remote" << remoteFilename << "wantsPermanentDeletion" << _item->_wantsPermanentDeletion;
auto headers = QMap<QByteArray, QByteArray>{};
if (_item->_locked == SyncFileItem::LockStatus::LockedItem) {
headers[QByteArrayLiteral("If")] = (QLatin1String("<") + propagator()->account()->davUrl().toString() + _item->_file + "> (<opaquelocktoken:" + _item->_lockToken.toUtf8() + ">)").toUtf8();
}
_job = new DeleteJob(propagator()->account(), propagator()->fullRemotePath(remoteFilename), headers, this);
+ _job->setSkipTrashbin(_item->_wantsPermanentDeletion);
connect(_job.data(), &DeleteJob::finishedSignal, this, &PropagateRemoteDelete::slotDeleteJobFinished);
propagator()->_activeJobList.append(this);
_job->start();
auto deleteJob = new DeleteJob(_propagator->account(), _propagator->fullRemotePath(filename), {}, this);
deleteJob->setFolderToken(folderToken());
deleteJob->setProperty(encryptedFileNamePropertyKey, filename);
+ deleteJob->setSkipTrashbin(true);
connect(deleteJob, &DeleteJob::finishedSignal, this, &PropagateRemoteDeleteEncryptedRootFolder::slotDeleteNestedRemoteItemFinished);
bool isPermissionsInvalid = false;
QString _discoveryResult;
+
+ /// if true, requests the file to be permanently deleted instead of moved to the trashbin
+ /// only relevant for when `_instruction` is set to `CSYNC_INSTRUCTION_REMOVE`
+ bool _wantsPermanentDeletion = false;
};
inline bool operator<(const SyncFileItemPtr &item1, const SyncFileItemPtr &item2)